package de.unijena.bioinf.lcms;

import de.unijena.bioinf.ChemistryBase.math.MatrixUtils;

import java.util.Arrays;
import java.util.Objects;

public class SavitzkyGolayFilter {

    // simple average smoothing
    public static final SavitzkyGolayFilter Window1Polynomial1 = new SavitzkyGolayFilter(
            new double[][]{
                    {1, -1}, {1, 0},{1,1}
            },
            new double[][]{{ 0.33333333,  0.33333333,  0.33333333},
            {-0.5       ,  0.        ,  0.5       }}
    );


    public static final SavitzkyGolayFilter Window2Polynomial2 = new SavitzkyGolayFilter(
            new double[][]{
                    { 1., -2.,  4.},
                    { 1., -1.,  1.},
                    { 1.,  0.,  0.},
                    { 1.,  1.,  1.},
                    { 1.,  2.,  4.}
            },
            new double[][]{
            {-0.08571429,  0.34285714,  0.48571429,  0.34285714, -0.08571429},
            {-0.2       , -0.1       ,  0.        ,  0.1       ,  0.2       },
            { 0.14285714, -0.07142857, -0.14285714, -0.07142857,  0.14285714}
        }
    );
    public static final SavitzkyGolayFilter Window3Polynomial2 = new SavitzkyGolayFilter(
            new double[][]{{ 1., -3.,  9.},
                    { 1., -2.,  4.},
                    { 1., -1.,  1.},
                    { 1.,  0.,  0.},
                    { 1.,  1.,  1.},
                    { 1.,  2.,  4.},
                    { 1.,  3.,  9.}},

    new double[][]{{-0.0952381 ,  0.14285714,  0.28571429,  0.33333333,  0.28571429,
            0.14285714, -0.0952381 },
        {-0.10714286, -0.07142857, -0.03571429,  0.        ,  0.03571429,
                0.07142857,  0.10714286},
        { 0.05952381,  0.        , -0.03571429, -0.04761905, -0.03571429,
                0.        ,  0.05952381}}
    );

    public static final SavitzkyGolayFilter Window3Polynomial3 = new SavitzkyGolayFilter(
            new double[][]{{  1.,  -3.,   9., -27.},
                    {  1.,  -2.,   4.,  -8.},
                    {  1.,  -1.,   1.,  -1.},
                    {  1.,   0.,   0.,   0.},
                    {  1.,   1.,   1.,   1.},
                    {  1.,   2.,   4.,   8.},
                    {  1.,   3.,   9.,  27.}},
            new double[][]{{-0.0952381 ,  0.14285714,  0.28571429,  0.33333333,  0.28571429,
                    0.14285714, -0.0952381 },
                    { 0.08730159, -0.26587302, -0.23015873,  0.        ,  0.23015873,
                            0.26587302, -0.08730159},
                    { 0.05952381,  0.        , -0.03571429, -0.04761905, -0.03571429,
                            0.        ,  0.05952381},
                    {-0.02777778,  0.02777778,  0.02777778,  0.        , -0.02777778,
                            -0.02777778,  0.02777778}}
    );

    public static final SavitzkyGolayFilter Window4Polynomial2 = new SavitzkyGolayFilter(new double[][]{{ 1., -4., 16.},
            { 1., -3.,  9.},
            { 1., -2.,  4.},
            { 1., -1.,  1.},
            { 1.,  0.,  0.},
            { 1.,  1.,  1.},
            { 1.,  2.,  4.},
            { 1.,  3.,  9.},
            { 1.,  4., 16.}},
            new double[][]{{-0.09090909,  0.06060606,  0.16883117,  0.23376623,  0.25541126,
                    0.23376623,  0.16883117,  0.06060606, -0.09090909},
                    {-0.06666667, -0.05      , -0.03333333, -0.01666667,  0.        ,
                            0.01666667,  0.03333333,  0.05      ,  0.06666667},
                    { 0.03030303,  0.00757576, -0.00865801, -0.01839827, -0.02164502,
                            -0.01839827, -0.00865801,  0.00757576,  0.03030303}}

    );

    public static final SavitzkyGolayFilter Window4Polynomial3 = new SavitzkyGolayFilter(new double[][]{{  1.,  -4.,  16., -64.},
            {  1.,  -3.,   9., -27.},
            {  1.,  -2.,   4.,  -8.},
            {  1.,  -1.,   1.,  -1.},
            {  1.,   0.,   0.,   0.},
            {  1.,   1.,   1.,   1.},
            {  1.,   2.,   4.,   8.},
            {  1.,   3.,   9.,  27.},
            {  1.,   4.,  16.,  64.}},
            new double[][]{{-0.09090909,  0.06060606,  0.16883117,  0.23376623,  0.25541126,
                    0.23376623,  0.16883117,  0.06060606, -0.09090909},
                    { 0.07239057, -0.11952862, -0.16245791, -0.10606061,  0.        ,
                            0.10606061,  0.16245791,  0.11952862, -0.07239057},
                    { 0.03030303,  0.00757576, -0.00865801, -0.01839827, -0.02164502,
                            -0.01839827, -0.00865801,  0.00757576,  0.03030303},
                    {-0.01178451,  0.00589226,  0.01094276,  0.00757576,  0.        ,
                            -0.00757576, -0.01094276, -0.00589226,  0.01178451}}
    );

    public static final SavitzkyGolayFilter Window8Polynomial2  = new SavitzkyGolayFilter(
        new double[][]{{ 1., -8., 64.},
                { 1., -7., 49.},
                { 1., -6., 36.},
                { 1., -5., 25.},
                { 1., -4., 16.},
                { 1., -3.,  9.},
                { 1., -2.,  4.},
                { 1., -1.,  1.},
                { 1.,  0.,  0.},
                { 1.,  1.,  1.},
                { 1.,  2.,  4.},
                { 1.,  3.,  9.},
                { 1.,  4., 16.},
                { 1.,  5., 25.},
                { 1.,  6., 36.},
                { 1.,  7., 49.},
                { 1.,  8., 64.}},
            new double[][]{{-6.50154799e-02, -1.85758514e-02,  2.16718266e-02,
                    5.57275542e-02,  8.35913313e-02,  1.05263158e-01,
                    1.20743034e-01,  1.30030960e-01,  1.33126935e-01,
                    1.30030960e-01,  1.20743034e-01,  1.05263158e-01,
                    8.35913313e-02,  5.57275542e-02,  2.16718266e-02,
                    -1.85758514e-02, -6.50154799e-02},
                    {-1.96078431e-02, -1.71568627e-02, -1.47058824e-02,
                            -1.22549020e-02, -9.80392157e-03, -7.35294118e-03,
                            -4.90196078e-03, -2.45098039e-03,  0.00000000e+00,
                            2.45098039e-03,  4.90196078e-03,  7.35294118e-03,
                            9.80392157e-03,  1.22549020e-02,  1.47058824e-02,
                            1.71568627e-02,  1.96078431e-02},
                    { 5.15995872e-03,  3.22497420e-03,  1.54798762e-03,
                            1.28998968e-04, -1.03199174e-03, -1.93498452e-03,
                            -2.57997936e-03, -2.96697626e-03, -3.09597523e-03,
                            -2.96697626e-03, -2.57997936e-03, -1.93498452e-03,
                            -1.03199174e-03,  1.28998968e-04,  1.54798762e-03,
                            3.22497420e-03,  5.15995872e-03}}
    );

    public static final SavitzkyGolayFilter Window16Polynomial2  = new SavitzkyGolayFilter(
            new double[][]{{  1, -16, 256},        {  1, -15, 225},        {  1, -14, 196},        {  1, -13, 169},        {  1, -12, 144},        {  1, -11, 121},        {  1, -10, 100},        {  1,  -9,  81},        {  1,  -8,  64},        {  1,  -7,  49},        {  1,  -6,  36},        {  1,  -5,  25},        {  1,  -4,  16},        {  1,  -3,   9},        {  1,  -2,   4},        {  1,  -1,   1},        {  1,   0,   0},        {  1,   1,   1},        {  1,   2,   4},        {  1,   3,   9},        {  1,   4,  16},        {  1,   5,  25},        {  1,   6,  36},        {  1,   7,  49},        {  1,   8,  64},        {  1,   9,  81},        {  1,  10, 100},        {  1,  11, 121},        {  1,  12, 144},        {  1,  13, 169},        {  1,  14, 196},        {  1,  15, 225},        {  1,  16, 256}},
            new double[][] { {-3.89610390e-02, -2.59740260e-02, -1.38248848e-02,         -2.51361542e-03,  7.95978215e-03,  1.75953079e-02,          2.63929619e-02,  3.43527440e-02,  4.14746544e-02,          4.77586929e-02,  5.32048597e-02,  5.78131546e-02,          6.15835777e-02,  6.45161290e-02,  6.66108085e-02,          6.78676163e-02,  6.82865522e-02,  6.78676163e-02,          6.66108085e-02,  6.45161290e-02,  6.15835777e-02,          5.78131546e-02,  5.32048597e-02,  4.77586929e-02,          4.14746544e-02,  3.43527440e-02,  2.63929619e-02,          1.75953079e-02,  7.95978215e-03, -2.51361542e-03,         -1.38248848e-02, -2.59740260e-02, -3.89610390e-02},         {-5.34759358e-03, -5.01336898e-03, -4.67914439e-03,         -4.34491979e-03, -4.01069519e-03, -3.67647059e-03,         -3.34224599e-03, -3.00802139e-03, -2.67379679e-03,         -2.33957219e-03, -2.00534759e-03, -1.67112299e-03,         -1.33689840e-03, -1.00267380e-03, -6.68449198e-04,         -3.34224599e-04,  0.00000000e+00,  3.34224599e-04,          6.68449198e-04,  1.00267380e-03,  1.33689840e-03,          1.67112299e-03,  2.00534759e-03,  2.33957219e-03,          2.67379679e-03,  3.00802139e-03,  3.34224599e-03,          3.67647059e-03,  4.01069519e-03,  4.34491979e-03,          4.67914439e-03,  5.01336898e-03,  5.34759358e-03},         { 7.63941940e-04,  6.20702827e-04,  4.86704946e-04,          3.61948298e-04,  2.46432884e-04,  1.40158703e-04,          4.31257547e-05, -4.46659602e-05, -1.23216442e-04,         -1.92525691e-04, -2.52593706e-04, -3.03420488e-04,         -3.45006038e-04, -3.77350354e-04, -4.00453437e-04,         -4.14315286e-04, -4.18935903e-04, -4.14315286e-04,         -4.00453437e-04, -3.77350354e-04, -3.45006038e-04,         -3.03420488e-04, -2.52593706e-04, -1.92525691e-04,         -1.23216442e-04, -4.46659602e-05,  4.31257547e-05,          1.40158703e-04,  2.46432884e-04,  3.61948298e-04,          4.86704946e-04,  6.20702827e-04,  7.63941940e-04}}
    );
    public static final SavitzkyGolayFilter Window32Polynomial2  = new SavitzkyGolayFilter(
            new double[][]{{1, -32, 1024}, {1, -31, 961}, {1, -30, 900}, {1, -29, 841}, {1, -28, 784}, {1, -27, 729}, {1, -26, 676}, {1, -25, 625}, {1, -24, 576}, {1, -23, 529}, {1, -22, 484}, {1, -21, 441}, {1, -20, 400}, {1, -19, 361}, {1, -18, 324}, {1, -17, 289}, {1, -16, 256}, {1, -15, 225}, {1, -14, 196}, {1, -13, 169}, {1, -12, 144}, {1, -11, 121}, {1, -10, 100}, {1, -9, 81}, {1, -8, 64}, {1, -7, 49}, {1, -6, 36}, {1, -5, 25}, {1, -4, 16}, {1, -3, 9}, {1, -2, 4}, {1, -1, 1}, {1, 0, 0}, {1, 1, 1}, {1, 2, 4}, {1, 3, 9}, {1, 4, 16}, {1, 5, 25}, {1, 6, 36}, {1, 7, 49}, {1, 8, 64}, {1, 9, 81}, {1, 10, 100}, {1, 11, 121}, {1, 12, 144}, {1, 13, 169}, {1, 14, 196}, {1, 15, 225}, {1, 16, 256}, {1, 17, 289}, {1, 18, 324}, {1, 19, 361}, {1, 20, 400}, {1, 21, 441}, {1, 22, 484}, {1, 23, 529}, {1, 24, 576}, {1, 25, 625}, {1, 26, 676}, {1, 27, 729}, {1, 28, 784}, {1, 29, 841}, {1, 30, 900}, {1, 31, 961}, {1, 32, 1024}},
            new double[][] {{-2.13547646e-02, -1.79104478e-02, -1.45754743e-02,        -1.13498442e-02, -8.23355749e-03, -5.22661418e-03,        -2.32901427e-03,  4.59242250e-04,  3.13815538e-03,         5.70772511e-03,  8.16795145e-03,  1.05188344e-02,         1.27603740e-02,  1.48925701e-02,  1.69154229e-02,         1.88289323e-02,  2.06330982e-02,  2.23279208e-02,         2.39134000e-02,  2.53895358e-02,  2.67563282e-02,         2.80137773e-02,  2.91618829e-02,  3.02006451e-02,         3.11300640e-02,  3.19501394e-02,  3.26608715e-02,         3.32622601e-02,  3.37543054e-02,  3.41370073e-02,         3.44103658e-02,  3.45743808e-02,  3.46290525e-02,         3.45743808e-02,  3.44103658e-02,  3.41370073e-02,         3.37543054e-02,  3.32622601e-02,  3.26608715e-02,         3.19501394e-02,  3.11300640e-02,  3.02006451e-02,         2.91618829e-02,  2.80137773e-02,  2.67563282e-02,         2.53895358e-02,  2.39134000e-02,  2.23279208e-02,         2.06330982e-02,  1.88289323e-02,  1.69154229e-02,         1.48925701e-02,  1.27603740e-02,  1.05188344e-02,         8.16795145e-03,  5.70772511e-03,  3.13815538e-03,         4.59242250e-04, -2.32901427e-03, -5.22661418e-03,        -8.23355749e-03, -1.13498442e-02, -1.45754743e-02,        -1.79104478e-02, -2.13547646e-02},       {-1.39860140e-03, -1.35489510e-03, -1.31118881e-03,        -1.26748252e-03, -1.22377622e-03, -1.18006993e-03,        -1.13636364e-03, -1.09265734e-03, -1.04895105e-03,        -1.00524476e-03, -9.61538462e-04, -9.17832168e-04,        -8.74125874e-04, -8.30419580e-04, -7.86713287e-04,        -7.43006993e-04, -6.99300699e-04, -6.55594406e-04,        -6.11888112e-04, -5.68181818e-04, -5.24475524e-04,        -4.80769231e-04, -4.37062937e-04, -3.93356643e-04,        -3.49650350e-04, -3.05944056e-04, -2.62237762e-04,        -2.18531469e-04, -1.74825175e-04, -1.31118881e-04,        -8.74125874e-05, -4.37062937e-05,  0.00000000e+00,         4.37062937e-05,  8.74125874e-05,  1.31118881e-04,         1.74825175e-04,  2.18531469e-04,  2.62237762e-04,         3.05944056e-04,  3.49650350e-04,  3.93356643e-04,         4.37062937e-04,  4.80769231e-04,  5.24475524e-04,         5.68181818e-04,  6.11888112e-04,  6.55594406e-04,         6.99300699e-04,  7.43006993e-04,  7.86713287e-04,         8.30419580e-04,  8.74125874e-04,  9.17832168e-04,         9.61538462e-04,  1.00524476e-03,  1.04895105e-03,         1.09265734e-03,  1.13636364e-03,  1.18006993e-03,         1.22377622e-03,  1.26748252e-03,  1.31118881e-03,         1.35489510e-03,  1.39860140e-03},       { 1.04373239e-04,  9.45882476e-05,  8.51138911e-05,         7.59501692e-05,  6.70970820e-05,  5.85546295e-05,         5.03228115e-05,  4.24016282e-05,  3.47910796e-05,         2.74911656e-05,  2.05018862e-05,  1.38232414e-05,         7.45523134e-06,  1.39785588e-06, -4.34888495e-06,        -9.78499113e-06, -1.49104627e-05, -1.97252996e-05,        -2.42295018e-05, -2.84230695e-05, -3.23060025e-05,        -3.58783008e-05, -3.91399645e-05, -4.20909936e-05,        -4.47313880e-05, -4.70611478e-05, -4.90802730e-05,        -5.07887635e-05, -5.21866194e-05, -5.32738406e-05,        -5.40504272e-05, -5.45163791e-05, -5.46716965e-05,        -5.45163791e-05, -5.40504272e-05, -5.32738406e-05,        -5.21866194e-05, -5.07887635e-05, -4.90802730e-05,        -4.70611478e-05, -4.47313880e-05, -4.20909936e-05,        -3.91399645e-05, -3.58783008e-05, -3.23060025e-05,        -2.84230695e-05, -2.42295018e-05, -1.97252996e-05,        -1.49104627e-05, -9.78499113e-06, -4.34888495e-06,         1.39785588e-06,  7.45523134e-06,  1.38232414e-05,         2.05018862e-05,  2.74911656e-05,  3.47910796e-05,         4.24016282e-05,  5.03228115e-05,  5.85546295e-05,         6.70970820e-05,  7.59501692e-05,  8.51138911e-05,         9.45882476e-05,  1.04373239e-04}}
    );
    public static final SavitzkyGolayFilter Window32Polynomial3  = new SavitzkyGolayFilter(
            new double[][]{{1, -32, 1024, -32768}, {1, -31, 961, -29791}, {1, -30, 900, -27000}, {1, -29, 841, -24389}, {1, -28, 784, -21952}, {1, -27, 729, -19683}, {1, -26, 676, -17576}, {1, -25, 625, -15625}, {1, -24, 576, -13824}, {1, -23, 529, -12167}, {1, -22, 484, -10648}, {1, -21, 441, -9261}, {1, -20, 400, -8000}, {1, -19, 361, -6859}, {1, -18, 324, -5832}, {1, -17, 289, -4913}, {1, -16, 256, -4096}, {1, -15, 225, -3375}, {1, -14, 196, -2744}, {1, -13, 169, -2197}, {1, -12, 144, -1728}, {1, -11, 121, -1331}, {1, -10, 100, -1000}, {1, -9, 81, -729}, {1, -8, 64, -512}, {1, -7, 49, -343}, {1, -6, 36, -216}, {1, -5, 25, -125}, {1, -4, 16, -64}, {1, -3, 9, -27}, {1, -2, 4, -8}, {1, -1, 1, -1}, {1, 0, 0, 0}, {1, 1, 1, 1}, {1, 2, 4, 8}, {1, 3, 9, 27}, {1, 4, 16, 64}, {1, 5, 25, 125}, {1, 6, 36, 216}, {1, 7, 49, 343}, {1, 8, 64, 512}, {1, 9, 81, 729}, {1, 10, 100, 1000}, {1, 11, 121, 1331}, {1, 12, 144, 1728}, {1, 13, 169, 2197}, {1, 14, 196, 2744}, {1, 15, 225, 3375}, {1, 16, 256, 4096}, {1, 17, 289, 4913}, {1, 18, 324, 5832}, {1, 19, 361, 6859}, {1, 20, 400, 8000}, {1, 21, 441, 9261}, {1, 22, 484, 10648}, {1, 23, 529, 12167}, {1, 24, 576, 13824}, {1, 25, 625, 15625}, {1, 26, 676, 17576}, {1, 27, 729, 19683}, {1, 28, 784, 21952}, {1, 29, 841, 24389}, {1, 30, 900, 27000}, {1, 31, 961, 29791}, {1, 32, 1024, 32768}},
            new double[][] {{-2.13547646e-02, -1.79104478e-02, -1.45754743e-02,        -1.13498442e-02, -8.23355749e-03, -5.22661418e-03,        -2.32901427e-03,  4.59242250e-04,  3.13815538e-03,         5.70772511e-03,  8.16795145e-03,  1.05188344e-02,         1.27603740e-02,  1.48925701e-02,  1.69154229e-02,         1.88289323e-02,  2.06330982e-02,  2.23279208e-02,         2.39134000e-02,  2.53895358e-02,  2.67563282e-02,         2.80137773e-02,  2.91618829e-02,  3.02006451e-02,         3.11300640e-02,  3.19501394e-02,  3.26608715e-02,         3.32622601e-02,  3.37543054e-02,  3.41370073e-02,         3.44103658e-02,  3.45743808e-02,  3.46290525e-02,         3.45743808e-02,  3.44103658e-02,  3.41370073e-02,         3.37543054e-02,  3.32622601e-02,  3.26608715e-02,         3.19501394e-02,  3.11300640e-02,  3.02006451e-02,         2.91618829e-02,  2.80137773e-02,  2.67563282e-02,         2.53895358e-02,  2.39134000e-02,  2.23279208e-02,         2.06330982e-02,  1.88289323e-02,  1.69154229e-02,         1.48925701e-02,  1.27603740e-02,  1.05188344e-02,         8.16795145e-03,  5.70772511e-03,  3.13815538e-03,         4.59242250e-04, -2.32901427e-03, -5.22661418e-03,        -8.23355749e-03, -1.13498442e-02, -1.45754743e-02,        -1.79104478e-02, -2.13547646e-02},       { 3.13836003e-03,  2.33138606e-03,  1.59192639e-03,         9.17803148e-04,  3.06838452e-04, -2.43145580e-04,        -7.34326828e-04, -1.16888317e-03, -1.54899250e-03,        -1.87683268e-03, -2.15458160e-03, -2.38441715e-03,        -2.56851719e-03, -2.70905962e-03, -2.80822231e-03,        -2.86818315e-03, -2.89112001e-03, -2.87921077e-03,        -2.83463333e-03, -2.75956555e-03, -2.65618532e-03,        -2.52667052e-03, -2.37319903e-03, -2.19794873e-03,        -2.00309751e-03, -1.79082323e-03, -1.56330379e-03,        -1.32271707e-03, -1.07124094e-03, -8.11053289e-04,        -5.44331994e-04, -2.73254937e-04,  0.00000000e+00,         2.73254937e-04,  5.44331994e-04,  8.11053289e-04,         1.07124094e-03,  1.32271707e-03,  1.56330379e-03,         1.79082323e-03,  2.00309751e-03,  2.19794873e-03,         2.37319903e-03,  2.52667052e-03,  2.65618532e-03,         2.75956555e-03,  2.83463333e-03,  2.87921077e-03,         2.89112001e-03,  2.86818315e-03,  2.80822231e-03,         2.70905962e-03,  2.56851719e-03,  2.38441715e-03,         2.15458160e-03,  1.87683268e-03,  1.54899250e-03,         1.16888317e-03,  7.34326828e-04,  2.43145580e-04,        -3.06838452e-04, -9.17803148e-04, -1.59192639e-03,        -2.33138606e-03, -3.13836003e-03},       { 1.04373239e-04,  9.45882476e-05,  8.51138911e-05,         7.59501692e-05,  6.70970820e-05,  5.85546295e-05,         5.03228115e-05,  4.24016282e-05,  3.47910796e-05,         2.74911656e-05,  2.05018862e-05,  1.38232414e-05,         7.45523134e-06,  1.39785588e-06, -4.34888495e-06,        -9.78499113e-06, -1.49104627e-05, -1.97252996e-05,        -2.42295018e-05, -2.84230695e-05, -3.23060025e-05,        -3.58783008e-05, -3.91399645e-05, -4.20909936e-05,        -4.47313880e-05, -4.70611478e-05, -4.90802730e-05,        -5.07887635e-05, -5.21866194e-05, -5.32738406e-05,        -5.40504272e-05, -5.45163791e-05, -5.46716965e-05,        -5.45163791e-05, -5.40504272e-05, -5.32738406e-05,        -5.21866194e-05, -5.07887635e-05, -4.90802730e-05,        -4.70611478e-05, -4.47313880e-05, -4.20909936e-05,        -3.91399645e-05, -3.58783008e-05, -3.23060025e-05,        -2.84230695e-05, -2.42295018e-05, -1.97252996e-05,        -1.49104627e-05, -9.78499113e-06, -4.34888495e-06,         1.39785588e-06,  7.45523134e-06,  1.38232414e-05,         2.05018862e-05,  2.74911656e-05,  3.47910796e-05,         4.24016282e-05,  5.03228115e-05,  5.85546295e-05,         6.70970820e-05,  7.59501692e-05,  8.51138911e-05,         9.45882476e-05,  1.04373239e-04},       {-7.16286932e-06, -5.81983132e-06, -4.58338364e-06,        -3.45008788e-06, -2.41650565e-06, -1.47919853e-06,        -6.34728148e-07,  1.20343907e-07,  7.89456027e-07,         1.37604661e-06,  1.88355406e-06,  2.31541676e-06,         2.67507312e-06,  2.96596154e-06,  3.19152040e-06,         3.35518812e-06,  3.46040308e-06,  3.51060368e-06,         3.50922832e-06,  3.45971540e-06,  3.36550331e-06,         3.23003045e-06,  3.05673523e-06,  2.84905603e-06,         2.61043125e-06,  2.34429930e-06,  2.05409857e-06,         1.74326745e-06,  1.41524434e-06,  1.07346765e-06,         7.21375760e-07,  3.62407079e-07,  0.00000000e+00,        -3.62407079e-07, -7.21375760e-07, -1.07346765e-06,        -1.41524434e-06, -1.74326745e-06, -2.05409857e-06,        -2.34429930e-06, -2.61043125e-06, -2.84905603e-06,        -3.05673523e-06, -3.23003045e-06, -3.36550331e-06,        -3.45971540e-06, -3.50922832e-06, -3.51060368e-06,        -3.46040308e-06, -3.35518812e-06, -3.19152040e-06,        -2.96596154e-06, -2.67507312e-06, -2.31541676e-06,        -1.88355406e-06, -1.37604661e-06, -7.89456027e-07,        -1.20343907e-07,  6.34728148e-07,  1.47919853e-06,         2.41650565e-06,  3.45008788e-06,  4.58338364e-06,         5.81983132e-06,  7.16286932e-06}}
    );



    public static void main(String[] args) {
        final double[] signal = new double[]{ 0.32556523,  0.51598466,  0.72521671,  0.96011726,  0.88875221,
                0.44022408,  0.18625947, -0.21055245, -0.75693153, -0.86655758,
                -1.12279338, -0.68924353, -0.39634847,  0.13839553,  0.63508686,
                0.80043708,  0.83263882,  0.82852367,  0.2824472 , -0.1070761};
        final double[] filtered = Window2Polynomial2.apply(signal);
        System.out.println(Arrays.toString(filtered));
        final double[] filtered2 = Window3Polynomial2.apply(signal);
        System.out.println(Arrays.toString(filtered2));


    }


    protected final double[][] vandermondeMatrix, inverseMatrix;
    protected final int polynomialDegree, windowSize;

    public SavitzkyGolayFilter(double[][] vandermondeMatrix, double[][] inverseMatrix) {
        this.vandermondeMatrix = vandermondeMatrix;
        this.inverseMatrix = inverseMatrix;
        this.windowSize = vandermondeMatrix.length;
        this.polynomialDegree = vandermondeMatrix[0].length-1;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SavitzkyGolayFilter that = (SavitzkyGolayFilter) o;
        return polynomialDegree == that.polynomialDegree && windowSize == that.windowSize;
    }

    @Override
    public int hashCode() {
        return Objects.hash(polynomialDegree, windowSize);
    }

    public double[] applyExtended(double[] values) {
        final double[] result = new double[values.length];
        final int n = (windowSize-1)/2;
        final double[] bufferA = new double[polynomialDegree+1], bufferB = new double[windowSize];
        // apply to the first n datapoints
        final double[] bufferC = new double[windowSize];
        System.arraycopy(values, 0, bufferC, n, bufferC.length-n);
        for (int k=0; k < n; ++k) bufferC[k] = values[0];

        matvecmul(inverseMatrix, bufferC, 0, bufferA);
        MatrixUtils.matmul(vandermondeMatrix, bufferA, bufferB);
        System.arraycopy(bufferB,0,result,0,n+1);
        // apply to everything in between
        for (int j=0; j < values.length-windowSize; ++j) {
            result[j + n] = dot(inverseMatrix[0], values, j);
        }
        // apply to the last n datapoints

        System.arraycopy(values, values.length-n, bufferC, 0, n);
        for (int k=n; k < bufferC.length; ++k) bufferC[k] = values[values.length-1];

        int k = values.length-windowSize;
        matvecmul(inverseMatrix, bufferC, 0, bufferA);
        MatrixUtils.matmul(vandermondeMatrix, bufferA, bufferB);
        System.arraycopy(bufferB,n,result,k+n,n+1);
        return result;

    }

    public double[] apply(double[] values) {
        final double[] result = new double[values.length];
        final int n = (windowSize-1)/2;
        final double[] bufferA = new double[polynomialDegree+1], bufferB = new double[windowSize];
        // apply to the first n datapoints
        matvecmul(inverseMatrix, values, 0, bufferA);
        MatrixUtils.matmul(vandermondeMatrix, bufferA, bufferB);
        System.arraycopy(bufferB,0,result,0,n+1);
        // apply to everything in between
        for (int j=0; j < values.length-windowSize; ++j) {
            result[j + n] = dot(inverseMatrix[0], values, j);
        }
        // apply to the last n datapoints
        int k = values.length-windowSize;
        matvecmul(inverseMatrix, values, k, bufferA);
        MatrixUtils.matmul(vandermondeMatrix, bufferA, bufferB);
        System.arraycopy(bufferB,n,result,k+n,n+1);
        return result;
    }


    // we cannot calculate derivatives for points outside the domain
    public double computeFirstOrderDerivative(double[] values, int offset) {
        offset -= (windowSize-1)/2;
        if (offset < windowSize || offset > values.length - windowSize) return 0d;
        // otherwise the derivative is equal to the second coefficient
        return dot(inverseMatrix[1], values, offset);
    }

    // we cannot calculate derivatives for points outside the domain
    public double computeSecondOrderDerivative(double[] values, int offset) {
        offset -= (windowSize-1)/2;
        if (offset < 0 || offset > values.length - windowSize) return 0d;
        // otherwise the derivative is equal to the third coefficient
        return 2*dot(inverseMatrix[2], values, offset);
    }

    // we cannot calculate derivatives for points outside the domain
    public double computeFirstOrderDerivative(float[] values, int offset) {
        offset -= (windowSize-1)/2;
        if (offset < 0 || offset > values.length - windowSize) return 0d;
        // otherwise the derivative is equal to the second coefficient
        return dot(inverseMatrix[1], values, offset);
    }

    // we cannot calculate derivatives for points outside the domain
    public double computeSecondOrderDerivative(float[] values, int offset) {
        offset -= (windowSize-1)/2;
        if (offset < 0 || offset > values.length - windowSize) return 0d;
        // otherwise the derivative is equal to the third coefficient
        return 2*dot(inverseMatrix[2], values, offset);
    }



    private static void matvecmul(double[][] M, double[] V, int offset, double[] buffer) {
        for (int row=0; row < M.length; ++row) {
            double buf = 0d;
            final double[] vec = M[row];
            for (int j=0; j < vec.length; ++j) {
                buf += vec[j]*V[j+offset];
            }
            buffer[row] = buf;
        }
    }
    private static double dot(double[] U, double[] V, int offset) {
        double x = 0d;
        for (int i=0; i < U.length; ++i) {
            x += U[i] * V[i+offset];
        }
        return x;
    }


    public float[] apply(float[] values) {
        final float[] result = new float[values.length];
        final int n = (windowSize-1)/2;
        final double[] bufferA = new double[polynomialDegree+1], bufferB = new double[windowSize];
        // apply to the first n datapoints
        matvecmul(inverseMatrix, values, 0, bufferA);
        MatrixUtils.matmul(vandermondeMatrix, bufferA, bufferB);
        for (int k=0; k < n+1; ++k) result[k] = (float)bufferB[k];
        // apply to everything in between
        for (int j=0; j < values.length-windowSize; ++j) {
            result[j + n] = dot(inverseMatrix[0], values, j);
        }
        // apply to the last n datapoints
        int k = values.length-windowSize;
        matvecmul(inverseMatrix, values, k, bufferA);
        MatrixUtils.matmul(vandermondeMatrix, bufferA, bufferB);
        for (int j=0; j < n+1; ++j) result[k+n+j] = (float)bufferB[n+j];
        return result;
    }
    private static void matvecmul(double[][] M, float[] V, int offset, double[] buffer) {
        for (int row=0; row < M.length; ++row) {
            double buf = 0d;
            final double[] vec = M[row];
            for (int j=0; j < vec.length; ++j) {
                buf += vec[j]*V[j+offset];
            }
            buffer[row] = buf;
        }
    }
    private static float dot(double[] U, float[] V, int offset) {
        double x = 0d;
        for (int i=0; i < U.length; ++i) {
            x += U[i] * V[i+offset];
        }
        return (float)x;
    }


    public int getWindowSize() {
        return windowSize;
    }

    public int getRadius() {
        return (windowSize-1)/2;
    }

    public int getNumberOfDataPointsPerSide() {
        return (windowSize-1)/2;
    }

    public int getDegree() {
        return polynomialDegree;
    }


    public String toString() {
        return "Savitzky Golay, window = " + (windowSize-1)/2 + ", degree = " + polynomialDegree;
    }

}
